package bll

import (
	"context"
	"fmt"
	"go-micro.dev/v4/client"
	"gorm.io/gorm"
	"manager/client/grpc"
	log "manager/collector/logger"
	"manager/errors"
	"manager/lib/trace_log"
	"manager/model"
	"manager/model/entity"
	"manager/store"
	"manager/store/postgres"
	"manager/utils"
	"proto/dto"
	"time"
)

var (
	upgradeInfoMap = map[int]string{
		0:   "升级成功",
		1:   "文件下载失败",
		2:   "文件校验失败",
		3:   "文件大小超过剩余空间",
		4:   "已存在升级任务",
		5:   "flash初始化失败",
		100: "版本不一致",
	}
)

type ota struct {
	iFirmwaresTactic     store.IFirmwaresTactic
	iFirmware            store.IFirmware
	iEbike               store.IEbike
	iUpgradeRecord       store.IUpgradeRecord
	iDeviceUpgradeRecode store.IDeviceUpgradeRecode
}

var Ota = &ota{
	iFirmwaresTactic:     postgres.FirmwaresTactic,
	iFirmware:            postgres.Firmware,
	iEbike:               postgres.Ebike,
	iUpgradeRecord:       postgres.UpgradeRecord,
	iDeviceUpgradeRecode: postgres.DeviceUpgradeRecode,
}

// ShadowFirmwareVersionKey 设备影子中 固件版本key
const ShadowFirmwareVersionKey = "firmware_version"

func (a *ota) init() func() {
	return func() {}
}

func (a *ota) CheckVersion(ctx context.Context, in *model.OtaCheckVersionRequest) (*model.OtaCheckVersionResponse, error) {
	var (
		err   error
		u     = &model.UpgradePlanBySnResponse{}
		vk    = &dto.GetShadowAttributeKeysResponse{}
		eBike *entity.Ebike
	)
	defer func() {
		trace_log.DebugLogger(in.SNCode, "CheckVersion in:", utils.ToJsonString(in), "err:", err)
	}()

	eBike, err = a.iEbike.FindBySnCode(ctx, in.SNCode)
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		log.Error(err)
		return nil, errors.EbikeNotExist.Error()
	}
	//查询设备影子中的版本信息
	vk, err = grpc.SCmd.GetShadowAttributeKeys(ctx, &dto.GetShadowAttributeKeysRequest{
		DeviceName: in.SNCode,
		Key:        ShadowFirmwareVersionKey,
	})
	if err != nil || vk == nil {
		return nil, err
	}

	if len(vk.Value) == 0 {
		log.Errorf("No version information found,sn: %v, err %v", in.SNCode, err)
	}

	//获取升级计划
	u, err = getUpgradePlan(in.SNCode, vk.Value, eBike.TypeID)
	if err != nil {
		log.Error(err)
		return nil, nil
	}
	if u == nil || u.FirmwareId <= 0 {
		log.Infof("sn %v no upgrade version", in.SNCode)
		return nil, nil
	}
	//静默固件直接推送设备，下发固件地址
	if int(model.OtaSilence) == u.ReleaseType {
		go func() {
			defer utils.ExceptionCatch()
			agree := model.OtaAgree
			_ = Ota.Upgrade(context.Background(), &model.OtaUpgradeRequest{
				SNCode:    in.SNCode,
				Status:    &agree,
				TacticsId: u.TacticsId,
			})
		}()
		return nil, nil
	}
	out := &model.OtaCheckVersionResponse{
		Version:     u.Version,
		ReleaseType: u.ReleaseType,
		TacticsId:   u.TacticsId,
	}
	return out, err
}

func (a *ota) Upgrade(ctx context.Context, in *model.OtaUpgradeRequest) error {
	var (
		err error
		ety *entity.Ebike
		f   *entity.Firmware
		ft  *entity.FirmwaresTactic
	)

	defer func() {
		trace_log.DebugLogger(in.SNCode, "OTA Upgrade tacticsID,", in.TacticsId, "status:", in.Status, " err:", err)
	}()

	if in.Status == nil {
		return errors.Wrap(err, errors.ParameterErr.Error().Error())
	}

	log.Debugf("OTA Upgrade sn:%v,tacticsID:%v,status:%v", in.SNCode, in.TacticsId, in.Status)
	ety, err = a.iEbike.FindBySnCode(ctx, in.SNCode)
	if err != nil {
		log.Error(err)
		return errors.Wrap(err, errors.EbikeNotExist.Error().Error())
	}
	ft, err = a.iFirmwaresTactic.Find(ctx, &model.FirmwaresTacticInfoRequest{Id: in.TacticsId})
	if err != nil {
		log.Error(err)
		return errors.Wrap(err, errors.NoData.Error().Error())
	}
	//获取固件地址
	f, err = a.iFirmware.Find(ctx, &model.FirmwareInfoRequest{Id: ft.FirmwareId})
	if err != nil {
		log.Error(err)
		return errors.Wrap(err, errors.NoData.Error().Error())
	}
	status := model.OtaInProgress
	if model.OtaCancel == *in.Status {
		status = model.OtaAbolishment //自主升级用户取消本次升级
	} else {
		path := "http://smartemc.oss-cn-shenzhen.aliyuncs.com/" + f.Path
		//推送设备，下发固件地址
		err = RpcCall(ety.SNCode, func(addr string, err error) error {
			if err != nil {
				return err
			}

			_, err = grpc.Cmd.Upgrade(ctx, &dto.UpgradeRequest{
				Sn:   ety.SNCode,
				Addr: path,
			}, func(options *client.CallOptions) {
				options.Address = []string{addr}
			})
			if err != nil {
				log.Errorf("push device err %v", ety.SNCode, err)
				return errors.DeviceCommandFailed.Error()
			}
			return err
		})
	}
	_, err = Ota.iDeviceUpgradeRecode.Create(ctx, &entity.DeviceUpgradeRecode{
		FirmwareId:    f.Id,
		TacticsId:     ft.Id,
		SnCode:        ety.SNCode,
		Version:       f.Version,
		FirmwaresName: f.Name,
		Status:        status,
		CreatedAt:     time.Now().Unix(),
	})
	if err != nil {
		log.Errorf("add upgrade record err %v", ety.SNCode, err)
	}
	return err
}

// OTAUpgradeSuccess 升级成功
func (a *ota) OTAUpgradeSuccess(sn string, version string) (err error) {
	find, err := postgres.DeviceUpgradeRecode.Find(context.Background(), &model.DeviceUpgradeRecodeInfoRequest{
		SnCode:  &sn,
		Version: &version,
	})
	if err != nil {
		trace_log.DebugLogger(sn, "OTAUpgradeSuccess err:", err)
		return fmt.Errorf(fmt.Sprintf("OTAUpgradeSuccess err: %v", err))
	}

	if find.Id > 0 {
		err = postgres.DeviceUpgradeRecode.Update(context.Background(), find.Id, map[string]interface{}{
			"status": model.OtaSuccess,
		})
	} else {
		trace_log.DebugLogger(sn, "OTAUpgradeSuccess findId is zero, DeviceUpgradeRecode ID:", find.Id)
		return fmt.Errorf(fmt.Sprintf("OTAUpgradeSuccess findId is zero"))
	}
	return updateUpgradeStatus(find.TacticsId)
}

func (a *ota) OTAUpgradeFailed(sn string, reason string) error {
	// 是否有升级计划
	u, err := Ota.iFirmwaresTactic.FindUpgradePlanBySn(sn, []int{1})
	if err != nil {
		return fmt.Errorf(fmt.Sprintf("OTAUpgradeFailed reason:%v err: %v", reason, err))
	}
	if u == nil || u.FirmwareId <= 0 {
		return fmt.Errorf("OTAUpgradeFailed err: %v", "no upgrade plan")
	}

	find, err := postgres.DeviceUpgradeRecode.Find(context.Background(), &model.DeviceUpgradeRecodeInfoRequest{
		SnCode:  &sn,
		Version: &u.Version,
	})

	if err != nil {
		return fmt.Errorf(fmt.Sprintf("OTAUpgradeFailed err: %v", err))
	}

	trace_log.DebugLogger(sn, "OTAUpgradeFailed context reason:", reason, " find:", utils.ToJsonString(find), " u:", utils.ToJsonString(u))
	err = postgres.DeviceUpgradeRecode.Delete(context.Background(), find.Id)
	if err != nil {
		return fmt.Errorf(fmt.Sprintf("OTAUpgrade Delete Failed err: %v", err))
	}

	return updateUpgradeStatus(find.TacticsId)
}

// OTAUpgradeSuccessNew 处理升级计划
func (a *ota) OTAUpgradeSuccessNew(sn string, reason string) error {
	// 是否有升级计划
	u, err := Ota.iFirmwaresTactic.FindUpgradePlanBySn(sn, []int{1})
	if err != nil {
		return fmt.Errorf(fmt.Sprintf("OTAUpgradeFailed reason:%v err: %v", reason, err))
	}
	if u == nil || u.FirmwareId <= 0 {
		return fmt.Errorf("OTAUpgradeFailed err: %v", "no upgrade plan")
	}

	find, err := postgres.DeviceUpgradeRecode.Find(context.Background(), &model.DeviceUpgradeRecodeInfoRequest{
		SnCode:  &sn,
		Version: &u.Version,
	})

	if err != nil {
		return fmt.Errorf(fmt.Sprintf("OTAUpgradeFailed err: %v", err))
	}

	trace_log.DebugLogger(sn, "OTAUpgradeFailed context reason:", reason, " find:", utils.ToJsonString(find), " u:", utils.ToJsonString(u))
	// 判断进度，如果是单个升级直接执行，全部升级需要所有设备升级完成才能进行终止

	return FirmwaresTactic.Terminate(context.Background(), &model.FirmwaresTacticTerminateRequest{Id: u.TacticsId})
}

func (a *ota) GetInfo(result int) string {
	if v, ok := upgradeInfoMap[result]; ok {
		return v
	}
	return "其他原因"
}

func getUpgradePlan(snCode string, version string, typeId int64) (u *model.UpgradePlanBySnResponse, err error) {
	u, err = Ota.iFirmwaresTactic.FindUpgradePlanByAllAndSn(snCode)
	if err != nil {
		log.Error(err)
		return
	}
	if u != nil && u.FirmwareId > 0 && u.Version != version {
		return
	}
	u, err = Ota.iFirmwaresTactic.FindUpgradePlanBySn(snCode, []int{0, 1, 2, 3, 4, 5})
	if err != nil {
		log.Error(err)
		return
	}
	if u != nil && u.FirmwareId > 0 && u.Version != version {
		return
	}
	u, err = Ota.iFirmwaresTactic.FindUpgradePlanBySnAndType(snCode, typeId)
	if err != nil {
		log.Error(err)
		return
	}
	if u != nil && u.FirmwareId > 0 && u.Version != version {
		return
	}
	return nil, nil
}

// 检查并更新升级计划的执行进度，执行完成了就变更状态为已完成
func updateUpgradeStatus(tacticsId int64) error {
	otaProcess := model.OtaInProgress
	find, err := postgres.DeviceUpgradeRecode.Find(context.Background(), &model.DeviceUpgradeRecodeInfoRequest{
		TacticsId: &tacticsId,
		Status:    &otaProcess,
	})
	if find == nil || errors.Is(err, gorm.ErrRecordNotFound) {
		// 没有待执行状态的数据，则更新升级计划状态为终止（已完成）
		return FirmwaresTactic.Terminate(context.Background(), &model.FirmwaresTacticTerminateRequest{Id: tacticsId})
	} else if err != nil {
		log.Error(err)
		return fmt.Errorf(fmt.Sprintf("updateUpgradeStatus err: %v", err))
	}
	return nil
}
